package com.brzjomo.embraceofthevoid.mixin;

import com.brzjomo.embraceofthevoid.EmbraceOfTheVoid;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.state.property.Properties;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.BlockLocating;
import net.minecraft.world.Heightmap;
import net.minecraft.world.TeleportTarget;
import net.minecraft.world.World;
import net.minecraft.world.border.WorldBorder;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.dimension.NetherPortal;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;

@Mixin(Entity.class)
abstract class EntityMixin {
    /**
     * @param destination The destination server world
     * @return TeleportTarget
     * {@code @reason}  Original method can't handle custom dimensions(Mods' dimensions)
     * @author Brzjomo
     */
    @Overwrite
    public TeleportTarget getTeleportTarget(ServerWorld destination) {
        if (EmbraceOfTheVoid.IS_DEBUG) {
            EmbraceOfTheVoid.LOGGER.info("Hack In !!!");
        }
        boolean bl3;
        boolean bl2;
        boolean bl = ((Entity) (Object) this).getWorld().getRegistryKey() == World.END && destination.getRegistryKey() == World.OVERWORLD;
        bl2 = destination.getRegistryKey() == World.END;
        if (bl || bl2) {
            BlockPos blockPos = bl2 ? ServerWorld.END_SPAWN_POS : destination.getTopPosition(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, destination.getSpawnPos());
            return new TeleportTarget(new Vec3d((double) blockPos.getX() + 0.5, blockPos.getY(), (double) blockPos.getZ() + 0.5), ((Entity) (Object) this).getVelocity(), ((Entity) (Object) this).getYaw(), ((Entity) (Object) this).getPitch());
        }
        bl3 = destination.getRegistryKey() == World.NETHER;
        if (((Entity) (Object) this).getWorld().getRegistryKey() != World.NETHER && !bl3) {
            BlockPos blockPos = destination.getTopPosition(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, destination.getSpawnPos());
            return new TeleportTarget(new Vec3d((double) blockPos.getX() + 0.5, blockPos.getY(), (double) blockPos.getZ() + 0.5), ((Entity) (Object) this).getVelocity(), ((Entity) (Object) this).getYaw(), ((Entity) (Object) this).getPitch());
        }
        WorldBorder worldBorder = destination.getWorldBorder();
        double d = DimensionType.getCoordinateScaleFactor(((Entity) (Object) this).getWorld().getDimension(), destination.getDimension());
        BlockPos blockPos2 = worldBorder.clamp(((Entity) (Object) this).getX() * d, ((Entity) (Object) this).getY(), ((Entity) (Object) this).getZ() * d);
        return ((Entity) (Object) this).getPortalRect(destination, blockPos2, bl3, worldBorder).map(rect -> {
            Vec3d vec3d;
            Direction.Axis axis;
            BlockState blockState = ((Entity) (Object) this).getWorld().getBlockState(((Entity) (Object) this).lastNetherPortalPosition);
            if (blockState.contains(Properties.HORIZONTAL_AXIS)) {
                axis = blockState.get(Properties.HORIZONTAL_AXIS);
                BlockLocating.Rectangle rectangle = BlockLocating.getLargestRectangle(((Entity) (Object) this).lastNetherPortalPosition, axis, 21, Direction.Axis.Y, 21, pos -> ((Entity) (Object) this).getWorld().getBlockState(pos) == blockState);
                vec3d = ((Entity) (Object) this).positionInPortal(axis, rectangle);
            } else {
                axis = Direction.Axis.X;
                vec3d = new Vec3d(0.5, 0.0, 0.0);
            }
            return NetherPortal.getNetherTeleportTarget(destination, rect, axis, vec3d, ((Entity) (Object) this), ((Entity) (Object) this).getVelocity(), ((Entity) (Object) this).getYaw(), ((Entity) (Object) this).getPitch());
        }).orElse(null);
    }
}
